import os,re

class TextMenu:
    def __init__(self, title='', width=0):
        self.title = title          # Menu title
        self.width = width          # Menu Width
        self.ansi_re = re.compile(r'\033\[[;\d]*[A-Za-z]')
        self.adjust_width(title)    # Adjusts width to the title length if needed
        self.commands = {}          # Options commands (acts like switch case)
        self.text = []              # Menu text content
        self.options = {
            "CLEAR_SCREEN":True,    # Clear screen before displaying menu
            "BORDER":False,         # Draw borders around the menu
            "TITLE_ALIGN":"left",   # left, right or center
            "PADDING_X":0,          # Space added between text and border
            "PADDING_Y":0,          # Space added between borders, title and options
            "MARGIN_X":0,           # Space between menu and left screen border
            "MARGIN_Y":0,           # Space betwwen menu, top screen border and what will be displayed below the menu
            "HLINE_CHAR":"-",       # Char used to drop the horizontal lines
            "VLINE_CHAR":"|",       # Char used to draw the vertical lines
            "CORNER_CHAR":"+",      # Char used to draw the border corners
        }

    def get_raw_width(self,text):
        '''
        Returns length of the string without ANSI special characters
        '''
        return len(self.ansi_re.sub('',text))

    def adjust_width(self,textline):
        '''
        Updates width of the menu to match the given line if it's larger than the actual width.
        '''
        stripped_length = self.get_raw_width(textline)
        if stripped_length > self.width:
            self.width = stripped_length

    def set_title(self,title):
        '''
        Updates the title value of the menu and updates its width to match the title
        '''
        self.title = title
        self.adjust_width(title)

    def add_textLine(self,textline):
        '''
        Add a simple text line to the menu
        '''
        self.text.append(textline)
        self.adjust_width(textline)

    def add_option(self,textline,command,key):
        """
        Add an option to the menu.
        Usage: TextMenu.addOption(str textline, obj command, str key)
            textline    :   description of the option show in the menu
            command     :   reference of the command which must be launched when the option is used
            key         :   the character or string user must type to use the option
        """
        optionString = str(key) + ' - ' + textline
        self.adjust_width(optionString)
        self.commands[str(key)] = command
        self.text.append(optionString)

    def add_space(self):
        """
        Add a blank line
        """
        self.text.append('')

    def build(self,menu_list):
        """
        Crate menu from a list
        """
        for line in menu_list:
            if line[0] == 'title':
                self.set_title(line[1])
            elif line[0] == 'text':
                self.add_textLine(line[1])
            elif line[0] == 'option':
                self.add_option(line[1],line[2],line[3])
            elif line [0] == 'space':
                self.add_space()
            elif line[0] == 'width':
                self.width = line[1]

    def __hline(self):
        """
        Draw an horizontal line, used when borders are enabled
        """
        return  ' '*self.options["MARGIN_X"] + self.options["CORNER_CHAR"] + self.options["HLINE_CHAR"]*(self.width+self.options["PADDING_X"]*2) + self.options["CORNER_CHAR"]

    def __space(self):
        """
        Create a blank line formated following alignment, menu size, padding, ...
        """
        return self.__format_text(' ')

    def __format_text(self,text,align="left"):
        """
        Format a text line to match borders, padding, margin and alignment settings
        """
        line = ''
        # Adding X Margin
        line += ' '* self.options["MARGIN_X"]
        # Adding left border if enabled
        line += self.options["VLINE_CHAR"] * self.options["BORDER"]
        # Formating content, including alignement and padding
        #line += ' ' * self.options["PADDING_X"]
        if align == "center":
            line += '{:<{padding}}{:^{line_width}}{:>{padding}}'.format('',text,'',line_width=self.width+(len(text)-self.get_raw_width(text)),padding=self.options["PADDING_X"])
        elif align== "right":
            line += '{:>{line_width}}{:>{padding}}'.format(text,'',padding=self.options["PADDING_X"],line_width=self.width+(len(text)-self.get_raw_width(text))+self.options["PADDING_X"])
        else:
            line += '{:>{padding}}{:<{line_width}}'.format('',text,padding=self.options["PADDING_X"],line_width=self.width+(len(text)-self.get_raw_width(text))+self.options["PADDING_X"])
        #line += ' ' * self.options["PADDING_X"]
        # Add right border if enabled
        line += self.options["VLINE_CHAR"] * self.options["BORDER"]

        return line

    def reset(self):
        self.text = []
        self.commands = {}
        self.title = ''
        self.width = 0

    def display(self):
        """
        Display the menu
        """
        # Clear screen if configured
        if self.options["CLEAR_SCREEN"]:
            os.system('clear')
        text_display = []

        # Add space from top if MARGIN_Y > 0
        if self.options["MARGIN_Y"] > 0:
            for i in range(self.options["MARGIN_Y"]):
                text_display.append('')

        # Add top border if border is enable
        if self.options["BORDER"]:
            text_display.append(self.__hline())

        # Add vertical padding before the title line
        if self.options["PADDING_Y"] > 0:
            for i in range(self.options["PADDING_Y"]):
                text_display.append(self.__space())

        # Add title line with given alignment
        text_display.append(self.__format_text(self.title,align="center"))

        # Add vertical padding after the title line
        if self.options["PADDING_Y"] > 0:
            for i in range(self.options["PADDING_Y"]):
                text_display.append(self.__space())

        # Add separation line between title and options if border is enable
        if self.options["BORDER"]:
            text_display.append(self.__hline())
            # Add vertical padding between separation line and options
            if self.options["PADDING_Y"] > 0:
                for i in range(self.options["PADDING_Y"]):
                    text_display.append(self.__space())

        # Add options
        for line in self.text:
            text_display.append(self.__format_text(line))

        if self.options["BORDER"]:
            # Add vertical padding between options and bottom line
            if self.options["PADDING_Y"] > 0:
                for i in range(self.options["PADDING_Y"]):
                    text_display.append(self.__space())
            # Add bottom line
            text_display.append(self.__hline())

        # Add space from bottom line if MARGIN_Y > 0
        if self.options["MARGIN_Y"] > 0:
            for i in range(self.options["MARGIN_Y"]):
                text_display.append('')

        for line in text_display:
            print line
